-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Add CVSS4 support #12751
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Add CVSS4 support #12751
Conversation
I think it would best to keep the two fields separate for now. As you mentioned, there are likely some companies that are reliant on v3 for compliance purposes. We also want to avoid potentially breaking changes in existing automation.
While I don't love the idea of embedding another big snippet of html on the view finding page, it would be consistent with the current behavior of v3. Being consistent would be ideal, but I am not sure it is worth the cost. Another option would be to link out to that universal calculator for both calculators. @mtesauro what do you think? |
I also considered linking out, but some companies might not like some external services being accessed. We could also link out to our own copy of that UI calculator hosted from within Defect Dojo. We could get rid of the old ugly one and just open the new one in a window. We could start with that and then see if there is a need for more integration like auto transfer for vectors between screens and whatnot. That would be the easiest for the Pro UI as well. |
I agree with this as well
It's probably best to include whatever we what displayed by default in DefectDojo in the source code aka shipped in our containers as we cannot rely on outbound connectivity for any default feature of DefectDojo.
Other thoughts on the CVSS calculators:
|
🟡 Please give this pull request extra attention during review.This pull request contains multiple security findings including a potential ReDoS vulnerability in CVSS vector parsing, debug logging that might expose sensitive information, a command injection risk in a shell script, and a potential cross-site scripting (XSS) vulnerability in the
🟡 Potential Cross-Site Scripting in
|
Vulnerability | Potential Cross-Site Scripting |
---|---|
Description | The code is potentially vulnerable to XSS in the BulletListDisplayWidget class's render method. While the method uses mark_safe() , which suggests some safety, it directly interpolates the url and text values from the urls_dict into the HTML without explicit sanitization. If an attacker can control the urls_dict input, they could potentially inject malicious JavaScript or HTML. The use of f'<li style="list-style-type: disc;"><a href="{url}" target="_blank"><i class="fa fa-arrow-up-right-from-square" style="margin-right: 5px;"></i>{text}</a></li>' creates an opportunity for XSS by directly embedding unsanitized user-supplied values into the HTML structure. |
django-DefectDojo/dojo/forms.py
Lines 140 to 161 in d67ee52
EFFORT_FOR_FIXING_INVALID_CHOICE = _("Select valid choice: Low,Medium,High") | |
class BulletListDisplayWidget(forms.Widget): | |
def __init__(self, urls_dict=None, *args, **kwargs): | |
self.urls_dict = urls_dict or {} | |
super().__init__(*args, **kwargs) | |
def render(self, name, value, attrs=None, renderer=None): | |
if not self.urls_dict: | |
return "" | |
html = '<ul style="margin: 0; padding-left: 20px;">' | |
for url, text in self.urls_dict.items(): | |
html += f'<li style="list-style-type: disc;"><a href="{url}" target="_blank"><i class="fa fa-arrow-up-right-from-square" style="margin-right: 5px;"></i>{text}</a></li>' | |
html += "</ul>" | |
return mark_safe(html) | |
class MultipleSelectWithPop(forms.SelectMultiple): | |
def render(self, name, *args, **kwargs): | |
html = super().render(name, *args, **kwargs) |
ReDoS in CVSS Vector Parsing in dojo/utils.py
Vulnerability | ReDoS in CVSS Vector Parsing |
---|---|
Description | The parse_cvss_from_text function uses a regular expression that could potentially be vulnerable to excessive backtracking with specially crafted input. Complex or long input strings might cause significant CPU resource consumption, leading to a potential Denial of Service. |
django-DefectDojo/dojo/utils.py
Lines 15 to 27 in d67ee52
import bleach | |
import crum | |
import hyperlink | |
import vobject | |
from asteval import Interpreter | |
from auditlog.models import LogEntry | |
from cryptography.hazmat.backends import default_backend | |
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes | |
from cvss import CVSS2, CVSS3, CVSS4, CVSSError | |
from dateutil.parser import parse | |
from dateutil.relativedelta import MO, SU, relativedelta | |
from django.conf import settings |
Information Disclosure via Debug Logging in dojo/utils.py
Vulnerability | Information Disclosure via Debug Logging |
---|---|
Description | Debug logging in the CVSS parsing functions includes potentially sensitive information about CVSS vectors. If debug logging is enabled in a production environment, this could lead to unintended exposure of vulnerability details. |
django-DefectDojo/dojo/utils.py
Lines 2668 to 2752 in d67ee52
return response | |
# TEMPORARY: Local implementation until the upstream PR is merged & released: https://github.com/RedHatProductSecurity/cvss/pull/75 | |
def parse_cvss_from_text(text): | |
""" | |
Parses CVSS2, CVSS3, and CVSS4 vectors from arbitrary text and returns a list of CVSS objects. | |
Parses text for substrings that look similar to CVSS vector | |
and feeds these matches to CVSS constructor. | |
Args: | |
text (str): arbitrary text | |
Returns: | |
A list of CVSS objects. | |
""" | |
# Looks for substrings that resemble CVSS2, CVSS3, or CVSS4 vectors. | |
# CVSS3 and CVSS4 vectors start with a 'CVSS:x.x/' prefix and are matched by the optional non-capturing group. | |
# CVSS2 vectors do not include a prefix and are matched by raw vector pattern only. | |
# Minimum total match length is 26 characters to reduce false positives. | |
matches = re.compile(r"(?:CVSS:[3-4]\.\d/)?[A-Za-z:/]{26,}").findall(text) | |
cvsss = set() | |
for match in matches: | |
try: | |
if match.startswith("CVSS:4."): | |
cvss = CVSS4(match) | |
elif match.startswith("CVSS:3."): | |
cvss = CVSS3(match) | |
else: | |
cvss = CVSS2(match) | |
cvsss.add(cvss) | |
except (CVSSError, KeyError): | |
pass | |
return list(cvsss) | |
def parse_cvss_data(cvss_vector_string: str) -> dict: | |
if not cvss_vector_string: | |
return {} | |
vectors = parse_cvss_from_text(cvss_vector_string) | |
if len(vectors) > 0: | |
vector = vectors[0] | |
# For CVSS2, environmental score is at index 2 | |
# For CVSS3, environmental score is at index 2 | |
# For CVSS4, only base score is available (at index 0) | |
# These CVSS2/3/4 objects do not have a version field (only a minor_version field) | |
major_version = cvssv2 = cvssv2_score = cvssv3 = cvssv3_score = cvssv4 = cvssv4_score = severity = None | |
if type(vector) is CVSS4: | |
major_version = 4 | |
cvssv4 = vector.clean_vector() | |
cvssv4_score = vector.scores()[0] | |
logger.debug("CVSS4 vector: %s, score: %s", cvssv4, cvssv4_score) | |
severity = vector.severities()[0] | |
elif type(vector) is CVSS3: | |
major_version = 3 | |
cvssv3 = vector.clean_vector() | |
cvssv3_score = vector.scores()[2] | |
severity = vector.severities()[0] | |
elif type(vector) is CVSS2: | |
# CVSS2 is not supported, but we return it anyway to allow parser to use the severity or score for other purposes | |
cvssv2 = vector.clean_vector() | |
cvssv2_score = vector.scores()[2] | |
severity = vector.severities()[0] | |
major_version = 2 | |
return { | |
"major_version": major_version, | |
"cvssv2": cvssv2, | |
"cvssv2_score": cvssv2_score, | |
"cvssv3": cvssv3, | |
"cvssv3_score": cvssv3_score, | |
"cvssv4": cvssv4, | |
"cvssv4_score": cvssv4_score, | |
"severity": severity, | |
} | |
logger.debug("No valid CVSS3 or CVSS4 vector found in %s", cvss_vector_string) | |
return {} | |
Command Injection Risk in run-unittest.sh
Vulnerability | Command Injection Risk |
---|---|
Description | The script constructs shell commands using user-controlled variables TEST_CASE and FAIL_FAST without proper input sanitization. An attacker could potentially inject arbitrary shell commands by manipulating these parameters, leading to remote code execution within the Docker container. |
django-DefectDojo/run-unittest.sh
Lines 1 to 6 in d67ee52
#!/usr/bin/env bash | |
unset TEST_CASE | |
unset FAIL_FAST | |
bash ./docker/docker-compose-check.sh | |
if [[ $? -eq 1 ]]; then exit 1; fi |
All finding details can be found in the DryRun Security Dashboard.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very nice job on this 😄
docs/content/en/open_source/contributing/how-to-write-a-parser.md
Outdated
Show resolved
Hide resolved
dojo/models.py
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@blakeaowens for your awareness of new fields
Fixes #12445
cvssv4
andcvss4_score
fields toFinding
modelcvssv3
fieldauditjs
parser + testsparse_cvss_data
to accomodate more use casesQUESTIONS:- Is it right to have the fields separated so we can support v3 anv v4 in parallel?- Or should we converge this intocvss
,cvss_score
andcvss_version
and prefer v4 over v3 if both are provided? This could be a problem for companies still tied to v3 in their compliance asessments?- We could do both. Leave the PR as is, but also add these "merged" fields where we take the values from v4 if present else from v3 (or None otherwise)- Do we want to embed a JS calculator again? The First/RedHat calculator is Vue based. The metaeffekt calculator is standalone JS:- https://www.first.org/cvss/calculator/4-0- https://www.metaeffekt.com/security/cvss/calculator/